Generalmente non si pensa ad Abode Acrobat come ad uno degli strumenti possibili per generare dei report graficamente elaborati, ma in cui i valori dei numeri e delle stringhe di testo provengono dalle sorgenti più disparate: ebbene si sbaglia!
Scopo di questo articolo è mostrarvi come, a partire da un modello consistente in un file .pdf in cui sono stati definiti dei campi (tranquilli, sui client e sul server non vi servirà l'acrobat exchange, la cui necessità è limitata solo alla macchina su cui vine creato il file .pdf da usare poi come modello), è possibile far giungere al client una sua versione completata con i valori desiderati. Unico vincolo è che sul client sia installato e funzionante l'Acrobat Reader. Prima di passare ad un poco di teoria ringrazio Ty Button per un suo articolo illuminante senza il quale non sarei qui a scrivervi.
Oltre a saper aprire e trattare i file PDF, l'acrobat reader è programmato per interpretare un altro formato: il Forms Data Format. Senza entrare troppo nello specifico, vi basti sapere che con un file fdf è possibile "poggiare" sopra ad un file PDF campi e bottoni con i relativi valori. In origine il formato era nato per utilizzare acrobat come front-end in applicazioni in cui si voleva che le form in cui inserire i dati fossero graficamente gradevoli, ma non è questo l'utilizzo che se ne farà qui. Noi utilizzeremo le sue capacità di chiamare un pdf (indirizzandolo con una URL) ed assegnare ai vari campi in esso presenti i valori da noi "decisi".
Non mi sto dunque prefiggendo di creare run-time un oggetto pdf (cosa molto più complicata, ma soprattutto determinante un carico considerevole per un server), ma un semplice file di testo che, diretto verso il client e indicato come fdf spingerà il browser ad aprire il plug-in Adobe Acrobat Reader che a sua volta recupererà dal server il .pdf "modello" e ne compilererà i campi come indicato nel fdf.
Risultato: grafica perfetta, carico limitato e soprattutto localizzato prevalentemente sul client, "stampabilità" assoluta. Rimane da risolvere un problemino: come creiamo i file fdf attraverso codice asp?
La Adobe ha creato un pacchetto sdk per gli fdf davvero notevole. Tra i tanti strumenti e componenti forniti ci sono due dll che sono le uniche cose necessarie sul server NT nel nostro caso: fdfacx.dll e fdftk.dll. Per le versioni più aggiornate, per il ToolKit completo e per l'intera documentazione qui.
Se invece sapete ciò che state per fare:
Io sto immaginando di ricevere i dati da una form, ma nulla v'impedisce (e ci mancherebbe!) di determinare i valori attraverso una query ad un database o in qualunque altro modo vi faccia comodo. Per non dilungarmi troppo non includo il codice della form con cui invio i dati e non vi descrivo come ho creato il documento pdf con i relativi campi. Potrete scaricare tutti i file necessari per eseguire l'esempio, analizzarli e modificarli come meglio credete, per adesso aguzzate le vostre doti deduttive e leggete i commenti al codice.
<%
Response.Buffer = true
%>
<html>
<head><%
Response.ContentType = "text/html"
%>
<title>Primo esempio: Metodo FDFSaveToFile</title>
</head>
<%
' ** Ricevo I dati che sono stati inseriti nella pagina precedente e
' ** inviati con il metodo post.
ValorePrimoCampo = Request.form("NomePrimoCampo")
ValoreSecondoCampo = Request.form("NomeSecondoCampo")
ValoreTerzoCampo = Request.form("NomeTerzoCampo")
ValoreQuartoCampo = Request.form("NomeQuartoCampo")
ValoreQuintoCampo = Request.form("NomeQuintoCampo")
' ** Valorizzo come meglio credo altre variabili
ValoreSestoCampo = "Questo campo è visibile, ma non stampabile. Istante di
generazione della pagina: "&Now()
' ** Creiamo un oggetto fdf ToolKit ActiveX
Set FdfAcx = Server.CreateObject("FdfApp.FdfApp")
' ** Creiamo un documeto fdf
Set myFdf = FdfAcx.FDFCreate
' ** Riempiamo i campi con i relativi valori
myFdf.fdfsetvalue "NomePrimoCampo", ValorePrimoCampo, False
myFdf.fdfsetvalue "NomeSecondoCampo", ValoreSecondoCampo, False
myFdf.fdfsetvalue "NomeTerzoCampo", ValoreTerzoCampo, False
myFdf.fdfsetvalue "NomeQuartoCampo", ValoreQuartoCampo, False
myFdf.fdfsetvalue "NomeQuintoCampo", ValoreQuintoCampo, False
myFdf.fdfsetvalue "NomeSestoCampo", ValoreSestoCampo, False
La funzione FDFSetValue ha la sintassi: Sub FDFSetValue(fieldName As String, newValue As String) dove fieldName è la stringa rappresentante il nome completo del campo (per esempio, employee.name.last) newValue è la stringa da usare come nuovo valore
' ** E' quì che indichiamo, nel fdf che stiamo creando, quale file
' ** pdf dovra' essere richiamato. L'indirizzo deve essere fornito
' ** come URL. Per indirizzamenti in relativo approfondite sulla
' ** documentazione. In questo caso scelgo di salvare il file fdf
' ** sul server e di creare un link su cui l'utente puo' cliccare.
' ** E' anche possibile dirigere direttamente l'fdf creato verso il
' ** buffer. Vi rimando alla documentazione del metodo FDFSavetoBuf
' ** e soprattutto all'esempio successivo.
' ** IL RESTO DEL CODICE NON NECESSITA ALTRE MODIFICE, MA E'
' ** NECESSARIO INDICARE QUI' IL PERCORSO VIRTUALE PRECISO PERCHE'
' ** VENGA RAGGIUNTO IL PDF.
myFDF.fdfSetFile "http://localhost/test/asppdf/test.pdf"
' ** Salvo il file fdf generato come CheckThis.fdf.
' ** Per evitare che due persone possano creare due file fdf con lo
' ** stesso nome dovrete decidere una convenzione o generare parte
' ** del nome in maniera univoca. Facendo finta che due richieste nello
' ** stesso secondo non siano possibili genero il nome come
' ** gGIORNOhORAmMINUTOsSECONDO.fdf il che mi risolve anche eventuali
' ** problemi di caching di Acrobat e del browser.
' ** E' inoltre necessario fornire un percorso "fisico" e che l'utente
' ** IUSR_nomeserver abbia i diritti di scrivere in quel percorso.
' ** Tutti problemi che non nascono se scegliamo di dirigere il fdf
' ** direttamente al buffer, dunque non approfondisco soluzioni piu'
' ** articolate per ripulire dai file creati.
' ** Immaginando di essere in C:\inetpub\wwwroot\test\asppdf
' ** il server.mappath("./") restituirà C:\inetpub\wwwroot\test\asppdf
FisicalPathCompleto = Server.MapPath("./")
NomeFile="g"&Day(Now())&"h"&Hour(Now())&"m"&Minute(Now())&"s"&second(Now())&".fdf"
myFDf.FDFSaveToFile FisicalPathCompleto&"\"&NomeFile
%>
<body>
<%
' ** A questo punto puntiamo il file con il percorso "virtuale" relativo
Response.Write("<a href="&NomeFile&">Per vedere il pdf
compilato premi qui</A>")
' ** E infine non scordiamoci di fare un poco di pulizia.
myfdf.fdfclose
set fdfacx = nothing
%>
</body>
</html>
Proviamo invece a spedire sul buffer direttamente il file FDF (è importante che dopo il codice non vi sia niente altro, è per questo che ho inserito un response.end che chiude il flusso; editor intelligenti quali FP98 tenderebbero sicuramente, rieditando la pagina attraverso di loro, ad aggiungere i tag <HTML> <BODY> etc etc, fornendo all'acrobat reader una versione corrotta del fdf e scatenando un errore). Ricordatevi che se volete evitare che sul client compaia il messaggio "salva con nome" bisogna impostare sul server il Mime Type per l'FDF come indicato in precedenza.
<%
' ** tutto il codice deve essere in testa al file asp, dunque non deve
' ** essere preceduto da nulla, altrimenti avremmo il classico errore da
' ** "header già impostati"
Response.buffer=True
Response.ContentType = "application/vnd.fdf"
' ** Ricevo I dati che sono stati inseriti nella pagina precedente e
' ** inviati con il metodo post.
ValorePrimoCampo = Request.form("NomePrimoCampo")
ValoreSecondoCampo = Request.form("NomeSecondoCampo")
ValoreTerzoCampo = Request.form("NomeTerzoCampo")
ValoreQuartoCampo = Request.form("NomeQuartoCampo")
ValoreQuintoCampo = Request.form("NomeQuintoCampo")
' ** Creo dinamicamente altri Valori
ValoreSestoCampo = "Questo campo è visibile, ma non stampabile. Istante di
generazione della pagina: "&Now()
' ** Creo un oggetto fdf ToolKit ActiveX
' ** ed, utilizzando il metodo FDFCreate, creo un FDF
Set FdfAcx = Server.CreateObject("FdfApp.FdfApp")
Set FDF = FdfAcx.FDFCreate
' ** Riempio i campi del documento con i valori desiderati
FDF.FDFSetValue "NomePrimoCampo", ValorePrimoCampo, False
FDF.FDFSetValue "NomeSecondoCampo", ValoreSecondoCampo, False
FDF.FDFSetValue "NomeTerzoCampo", ValoreTerzoCampo, False
FDF.FDFSetValue "NomeQuartoCampo", ValoreQuartoCampo, False
FDF.FDFSetValue "NomeQuintoCampo", ValoreQuintoCampo, False
FDF.FDFSetValue "NomeSestoCampo", ValoreSestoCampo, False
' ** Indico al fdf il percorso del file pdf in cui inserire i dati
' ** Sara' l'acrobat sul client a scaricare il pdf e riempirlo come
' ** indicato dal fdf (sono ripetitivo eh?).
' ** IL RESTO DEL CODICE NON NECESSITA ALTRE MODIFICE, MA E'
' ** NECESSARIO INDICARE QUI' IL PERCORSO VIRTUALE PRECISO PERCHE'
' ** VENGA RAGGIUNTO IL PDF.
FDF.FDFSetFile "http://localhost/test/asppdf/test.pdf"
' ** Dirigo l'fdf risultante direttamente sul Buffer verso il browser del
client
Response.BinaryWrite FDF.FDFSaveToBuf
FDF.FDFClose
set FdfAcx = nothing
' ** forzo il server ad ignorare qualunque altra stupidagine sia stata
' ** inserita a seguire
Response.end
%>
Mi rendo conto di aver sorvolato su alcune questioni non banali (quali l'utilizzo dei vari tipi di campo per le form nei pdf), ma spero di avervi dato lo spunto per approfondire un nuovo strumento, ed osservando il pdf allegato vi accorgerete in fondo è così.
D'altronde per creare i file pdf è necessario acquistare il pacchetto Adobe Acrobat e, se l'avete fatto, dubito che non vi siate messi a "giocare" con la creazione di form. Le applicazioni di un tale approccio ai pdf completati attraverso gli fdf sono innumerevoli.
Spero solo di avervi convinto che il carico lato server è davvero contenuto e per concludere vi ricordo che tutto quanto abbiamo fatto rimane lato client, squisitamente multipiattaforma, in pieno spirito Acrobat.
Per le reference sugli oggetti e sul FDF ToolKit vi consiglio: FDFToolKit Overview e FDFToolKit Reference (nella sezione Toolkit for ActiveX) che potrete trovare al sito sopra indicato. Il pdf allegato è stato creato con Acrobat, ma non ho testato la sua compatibilità con l'Acrobat Reader 3.